home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / emacssrc.zip / EMACSSRC.TAR / emacs-19.17 / lib-src / etags.c < prev    next >
C/C++ Source or Header  |  1993-09-23  |  37KB  |  1,703 lines

  1. /* Tags file maker to go with GNUmacs
  2.    Copyright (C) 1984, 1987, 1988 Free Software Foundation, Inc. and Ken Arnold
  3.  
  4.     This program is free software; you can redistribute it and/or modify
  5.     it under the terms of the GNU General Public License as published by
  6.     the Free Software Foundation; either version 1, or (at your option)
  7.     any later version.
  8.  
  9.     This program is distributed in the hope that it will be useful,
  10.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.     GNU General Public License for more details.
  13.  
  14.     You should have received a copy of the GNU General Public License
  15.     along with this program; if not, write to the Free Software
  16.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. In other words, you are welcome to use, share and improve this program.
  19. You are forbidden to forbid anyone else to use, share and improve
  20. what you give them.   Help stamp out software-hoarding!  */
  21.  
  22. /*
  23.  * HISTORY:
  24.  * 30-Oct-92    Mike Jones (mbj)
  25.  *    Removed dependence upon the ability to fseek().
  26.  * 
  27.  * 27-Oct-92    Mike Jones (mbj)
  28.  *    Ported to Windows NT.  Added -@ indirect file and -z echo features.
  29.  * 
  30.  */
  31.  
  32. #include <stdio.h>
  33. #include <ctype.h>
  34.  
  35. /* Define the symbol ETAGS to make the program "etags",
  36.  which makes emacs-style tag tables by default.
  37.  Define CTAGS to make the program "ctags" compatible with the usual one.
  38.  Define neither one to get behavior that depends
  39.  on the name with which the program is invoked
  40.  (but we don't normally compile it that way).  */
  41.  
  42. /* On VMS, CTAGS is not useful, so always do ETAGS.  */
  43. #if    defined(VMS) || defined(WINDOWSNT)
  44. #ifndef ETAGS
  45. #define ETAGS
  46. #endif
  47. #endif
  48.  
  49. /* Exit codes for success and failure.  */
  50.  
  51. #ifdef VMS
  52. #define    GOOD    (1)
  53. #define BAD    (0)
  54. #else
  55. #define    GOOD    (0)
  56. #define    BAD    (1)
  57. #endif
  58.  
  59. #define    reg    register
  60. #define    logical    char
  61.  
  62. #define    TRUE    (1)
  63. #define    FALSE    (0)
  64.  
  65. #define    iswhite(arg)    (_wht[arg])    /* T if char is white        */
  66. #define    begtoken(arg)    (_btk[arg])    /* T if char can start token    */
  67. #define    intoken(arg)    (_itk[arg])    /* T if char can be in token    */
  68. #define    endtoken(arg)    (_etk[arg])    /* T if char ends tokens    */
  69. #define    isgood(arg)    (_gd[arg])    /* T if char can be after ')'    */
  70.  
  71. #define    max(I1,I2)    (I1 > I2 ? I1 : I2)
  72.  
  73. /* cause token checking for typedef, struct, union, enum to distinguish
  74.    keywords from identifier-prefixes (e.g. struct vs struct_tag).  */
  75. #define istoken(s, tok, len) (!strncmp(s,tok,len) && endtoken(*((s)+(len))))
  76.  
  77. struct    nd_st {            /* sorting structure            */
  78.     char    *name;            /* function or type name    */
  79.     char    *file;            /* file name            */
  80.     logical f;            /* use pattern or line no    */
  81.     int    lno;            /* line number tag is on    */
  82.     long    cno;            /* character number line starts on */
  83.     char    *pat;            /* search pattern        */
  84.     logical    been_warned;        /* set if noticed dup        */
  85.     struct    nd_st    *left,*right;    /* left and right sons        */
  86. };
  87.  
  88. typedef    struct    nd_st    NODE;
  89.  
  90. int number; /* tokens found so far on line starting with # (including #) */
  91. logical gotone,                /* found a func already on line    */
  92.                     /* boolean "func" (see init)    */
  93.     _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
  94.  
  95.     /* typedefs are recognized using a simple finite automata,
  96.      * tydef is its state variable.
  97.      */
  98. typedef enum {none, begin, tag_ok, middle, end } TYST;
  99.  
  100. TYST tydef = none;
  101.  
  102. logical next_token_is_func;
  103.  
  104. char  searchar = '/';                 /* use /.../ searches           */
  105.  
  106. int    lineno;            /* line number of current line */
  107. long    charno;            /* current character number */
  108. long    linecharno;        /* character number of start of line */
  109. long    linecharno1 = -1L;    /* character number of start of line in lb1 */
  110.  
  111. char    *curfile,        /* current input file name        */
  112.     *outfile= 0,        /* output file                */
  113.     *white    = " \f\t\n",    /* white chars                */
  114.     *endtk    = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?",
  115.                 /* token ending chars            */
  116.     *begtk    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$",
  117.                 /* token starting chars            */
  118.     *intk    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789",
  119.                 /* valid in-token chars            */
  120.     *notgd    = ",;";        /* non-valid after-function chars    */
  121. char    *atfile = 0;        /* File containing filenames        */
  122.  
  123. int    file_num = 0;        /* current file number            */
  124. int    aflag = 0;        /* -a: append to tags */
  125. int    tflag = 0;        /* -t: create tags for typedefs */
  126. int    uflag = 0;        /* -u: update tags */
  127. int    wflag = 0;        /* -w: suppress warnings */
  128. int    vflag = 0;        /* -v: create vgrind style index output */
  129. int    xflag = 0;        /* -x: create cxref style output */
  130. int    eflag = 0;        /* -e: emacs style output */
  131. int    zflag = 0;        /* -z: echo filenames */
  132. int    atflag = 0;        /* -@: Indirect input file name */
  133.  
  134. /* Name this program was invoked with.  */
  135. char *progname;
  136.  
  137. FILE    *inf,            /* ioptr for current input file        */
  138.     *outf;            /* ioptr for tags file            */
  139. FILE    *atf;            /* ioptr for filenames file        */
  140.  
  141. NODE    *head;            /* the head of the sorted binary tree    */
  142.  
  143. char *savestr();
  144. char *savenstr ();
  145. char *rindex();
  146. char *index();
  147. char *concat ();
  148. void initbuffer ();
  149. long readline ();
  150. void find_entries();
  151. void add_node();
  152. void put_entries();
  153. void takeprec();
  154. void getit();
  155. void L_getit();
  156. void TEX_getit();
  157.  
  158. /* A `struct linebuffer' is a structure which holds a line of text.
  159.  `readline' reads a line from a stream into a linebuffer
  160.  and works regardless of the length of the line.  */
  161.  
  162. struct linebuffer
  163.   {
  164.     long size;
  165.     char *buffer;
  166.   };
  167.  
  168. struct linebuffer lb, lb1;
  169. struct linebuffer atfile_lb;
  170.  
  171. #if 0  /* VMS now provides the `system' function.  */
  172. #ifdef VMS
  173.  
  174. #include <descrip.h>
  175.  
  176. void
  177. system (buf)
  178.      char *buf;
  179. {
  180.   struct dsc$descriptor_s command =
  181.     {
  182.       strlen(buf), DSC$K_DTYPE_T, DSC$K_CLASS_S, buf
  183.     };
  184.  
  185.   LIB$SPAWN(&command);
  186. }
  187. #endif /* VMS */
  188. #endif /* 0 */
  189.  
  190. main(ac,av)
  191.      int    ac;
  192.      char    *av[];
  193. {
  194.   char cmd[100];
  195.   int i;
  196.   int fflag = 0;    /* Output file name */
  197.   char *this_file;
  198. #ifdef VMS
  199.   char got_err;
  200.  
  201.   extern char *gfnames();
  202.   extern char *massage_name();
  203. #endif
  204.  
  205.   progname = av[0];
  206.  
  207. #ifdef ETAGS
  208.   eflag = 1;
  209. #else
  210.   eflag = 0;
  211. #endif
  212.  
  213.   while (ac > 1 && av[1][0] == '-')
  214.     {
  215.       for (i=1; av[1][i]; i++)
  216.     {
  217.       switch(av[1][i])
  218.         {
  219. #if    (!defined(VMS)) && (!defined(WINDOWSNT))  /* These options are useful only with ctags,
  220.         and VMS can't input them, so just omit them.  */
  221.         case 'B':
  222.           searchar='?';
  223.           eflag = 0;
  224.           break;
  225.         case 'F':
  226.           searchar='/';
  227.           eflag = 0;
  228.           break;
  229. #endif
  230.         case 'a':
  231.           aflag++;
  232.           break;
  233.         case 'e':
  234.           eflag++;
  235.           break;
  236.         case 'f':
  237.           if (fflag > 0)
  238.         {
  239.           fprintf(stderr,
  240.               "%s: -f flag may only be given once\n", progname);
  241.           goto usage;
  242.         }
  243.           fflag++, ac--; av++;
  244.           if (ac <= 1 || av[1][0] == '\0')
  245.         {
  246.           fprintf(stderr,
  247.               "%s: -f flag must be followed by a filename\n",
  248.               progname);
  249.           goto usage;
  250.         }
  251.           outfile = av[1];
  252.           goto end_loop;
  253.         case 't':
  254.           tflag++;
  255.           break;
  256. #if    (!defined(VMS)) && (!defined(WINDOWSNT))
  257.         case 'u':
  258.           uflag++;
  259.           eflag = 0;
  260.           break;
  261. #endif
  262.         case 'w':
  263.           wflag++;
  264.           break;
  265.         case 'v':
  266.           vflag++;
  267.           xflag++;
  268.           eflag = 0;
  269.           break;
  270.         case 'x':
  271.           xflag++;
  272.           eflag = 0;
  273.           break;
  274.         case 'z':
  275.           zflag++;
  276.           break;
  277.         case '@':
  278.           if (atflag > 0)
  279.         {
  280.           fprintf(stderr,
  281.               "%s: -@ flag may only be given once\n", progname);
  282.           goto usage;
  283.         }
  284.           atflag++, ac--; av++;
  285.           if (ac <= 1 || av[1][0] == '\0')
  286.         {
  287.           fprintf(stderr,
  288.               "%s: -@ flag must be followed by a filename\n",
  289.               progname);
  290.           goto usage;
  291.         }
  292.           atfile = av[1];
  293.           goto end_loop;
  294.         default:
  295.           goto usage;
  296.         }
  297.     }
  298.     end_loop: ;
  299.       ac--; av++;
  300.     }
  301.  
  302.   if (ac <= 1 && ! atflag)
  303.     {
  304.     usage:
  305. #if    defined(VMS) || defined(WINDOWSNT)
  306.       fprintf (stderr, "Usage: %s [-aetwvx] [-@ filenames_file] [-f outfile] file ...\n", progname);
  307. #else
  308.       fprintf (stderr, "Usage: %s [-BFaetuwvx] [-@ filenames_file] [-f outfile] file ...\n", progname);
  309. #endif
  310.       exit(BAD);
  311.     }
  312.  
  313.   if (outfile == 0)
  314.     {
  315.       outfile = eflag ? "TAGS" : "tags";
  316.     }
  317.  
  318.   init();            /* set up boolean "functions"        */
  319.  
  320.   initbuffer (&lb);
  321.   initbuffer (&lb1);
  322.   initbuffer (&atfile_lb);
  323.   /*
  324.    * loop through files finding functions
  325.    */
  326.   if (eflag)
  327.     {
  328.       outf = fopen (outfile, aflag ? "a" : "w");
  329.       if (!outf)
  330.     {
  331.       fprintf (stderr, "%s: ", progname);
  332.       perror (outfile);
  333.       exit (BAD);
  334.     }
  335.     }
  336.   if (atflag)
  337.     /* Handle -@ filelist_file construct */
  338.     {
  339.       int atlen;
  340.       atf = fopen(atfile, "r");
  341.       if (atf == NULL)
  342.     {
  343.       fprintf (stderr, "%s: ", atfile);
  344.       perror(atfile);
  345.       exit(BAD);
  346.     }
  347.  
  348.  
  349.       while (!feof (atf))
  350.     {
  351.       atlen = readline(&atfile_lb, atf);
  352.       if (atlen == 0) continue;        /* Empty line */ 
  353.       this_file = atfile_lb.buffer;
  354.       if (this_file[0] == '#') continue;    /* Comment line */
  355.  
  356.       find_entries (this_file);
  357.     }
  358.  
  359.       fclose(atf);
  360.     }
  361.  
  362.   file_num = 1;
  363. #ifdef VMS
  364.   for (ac--, av++;
  365.        (this_file = gfnames (&ac, &av, &got_err)) != NULL; file_num++)
  366.     {
  367.       if (got_err)
  368.     {
  369.       error("Can't find file %s\n", this_file);
  370.       ac--, av++;
  371.     }
  372.       else
  373.     {
  374.       this_file = massage_name (this_file);
  375. #else     
  376.   for (; file_num < ac; file_num++)
  377.     {
  378.       this_file = av[file_num];
  379.       if (1)
  380.     {
  381. #endif
  382.       find_entries (this_file);
  383.     }
  384.     }
  385.  
  386.   if (eflag)
  387.     {
  388.       fclose (outf);
  389.       exit (GOOD);
  390.     }
  391.  
  392.   if (xflag)
  393.     {
  394.       put_entries(head);
  395.       exit(GOOD);
  396.     }
  397.   if (uflag)
  398.     {
  399.       for (i=1; i<ac; i++)
  400.     {
  401.       sprintf(cmd,
  402.           "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
  403.           outfile, av[i], outfile);
  404.       system(cmd);
  405.     }
  406.       aflag++;
  407.     }
  408.   outf = fopen(outfile, aflag ? "a" : "w");
  409.   if (outf == NULL)
  410.     {
  411.       fprintf (stderr, "%s: ", outfile);
  412.       perror(outfile);
  413.       exit(BAD);
  414.     }
  415.   put_entries(head);
  416.   fclose(outf);
  417. #if    (!defined(VMS)) && (!defined(WINDOWSNT))
  418.   if (uflag)
  419.     {
  420.       sprintf(cmd, "sort %s -o %s", outfile, outfile);
  421.       system(cmd);
  422.     }
  423. #endif
  424.   exit(GOOD);
  425. }
  426.  
  427. /*
  428.  * This routine sets up the boolean psuedo-functions which work
  429.  * by seting boolean flags dependent upon the corresponding character
  430.  * Every char which is NOT in that string is not a white char.  Therefore,
  431.  * all of the array "_wht" is set to FALSE, and then the elements
  432.  * subscripted by the chars in "white" are set to TRUE.  Thus "_wht"
  433.  * of a char is TRUE if it is the string "white", else FALSE.
  434.  */
  435. init()
  436. {
  437.  
  438.   reg char *sp;
  439.   reg int i;
  440.  
  441.   for (i = 0; i < 0177; i++)
  442.     {
  443.       _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
  444.       _gd[i] = TRUE;
  445.     }
  446.   for (sp = white; *sp; sp++)
  447.     _wht[*sp] = TRUE;
  448.   for (sp = endtk; *sp; sp++)
  449.     _etk[*sp] = TRUE;
  450.   for (sp = intk; *sp; sp++)
  451.     _itk[*sp] = TRUE;
  452.   for (sp = begtk; *sp; sp++)
  453.     _btk[*sp] = TRUE;
  454.   for (sp = notgd; *sp; sp++)
  455.     _gd[*sp] = FALSE;
  456.   _wht[0] = _wht['\n'];
  457.   _etk[0] = _etk['\n'];
  458.   _btk[0] = _btk['\n'];
  459.   _itk[0] = _itk['\n'];
  460.   _gd[0] = _gd['\n'];
  461. }
  462.  
  463. /*
  464.  * This routine opens the specified file and calls the function
  465.  * which finds the function and type definitions.
  466.  */
  467. void
  468. find_entries (file)
  469.      char *file;
  470. {
  471.   char *cp;
  472.  
  473.   if ((inf=fopen(file,"r")) == NULL)
  474.     {
  475.       fprintf (stderr, "%s: ", progname);
  476.       perror(file);
  477.       return;
  478.     }
  479.   if (zflag)
  480.     fprintf(stderr, "%s: Tagging \"%s\"\n", progname, file);
  481.  
  482.   curfile = savestr(file);
  483.   cp = rindex(file, '.');
  484.   /* .tex, .aux or .bbl implies LaTeX source code */
  485.   if (cp && (!strcmp (cp + 1, "tex") || !strcmp (cp + 1, "aux")
  486.          || !strcmp (cp + 1, "bbl")))
  487.     {
  488.       TEX_funcs(inf);
  489.       goto find_done;
  490.     }
  491.   /* .l or .el or .lisp (or .cl or .clisp or ...) implies lisp source code */
  492.   if (cp && (!strcmp (cp + 1, "l") ||
  493.          !strcmp (cp + 1, "el") ||
  494.          !strcmp (cp + 1, "lsp") ||
  495.          !strcmp (cp + 1, "lisp") ||
  496.          !strcmp (cp + 1, "cl") ||
  497.          !strcmp (cp + 1, "clisp")))
  498.     {
  499.       L_funcs(inf);
  500.       goto find_done;
  501.     }
  502.   /* .scm or .sm or .scheme implies scheme source code */
  503.   if (cp && (!strcmp (cp + 1, "sm")
  504.          || !strcmp (cp + 1, "scm")
  505.          || !strcmp (cp + 1, "scheme")
  506.          || !strcmp (cp + 1, "t")
  507.          || !strcmp (cp + 1, "sch")
  508.          || !strcmp (cp + 1, "SM")
  509.          || !strcmp (cp + 1, "SCM")
  510.              /* The `SCM' or `scm' prefix with a version number */
  511.              || (cp[-1] == 'm' && cp[-2] == 'c' && cp[-3] == 's'
  512.          && string_numeric_p (cp + 1))
  513.              || (cp[-1] == 'M' && cp[-2] == 'C' && cp[-3] == 'S'
  514.          && string_numeric_p (cp + 1))))
  515.     {
  516.       Scheme_funcs(inf);
  517.       goto find_done;
  518.     }
  519.   /* if not a .c or .h or .y file, try fortran */
  520.   if (cp && (cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y')
  521.       && cp[2] == '\0')
  522.     {
  523.       if (PF_funcs(inf) != 0)
  524.     {
  525.       goto find_done;
  526.     }
  527.       rewind(inf);    /* no fortran tags found, try C */
  528.     }
  529.   C_entries();
  530.  
  531. find_done:
  532.   fclose(inf);
  533.   if (eflag)
  534.     {
  535.       fprintf (outf, "\f\n%s,%d\n",
  536.            file, total_size_of_entries (head));
  537.       put_entries (head);
  538.       free_tree (head);
  539.       head = NULL;
  540.     }
  541.  
  542. }
  543.  
  544. /* Nonzero if string STR is composed of digits.  */
  545.  
  546. int
  547. string_numeric_p (str)
  548.      char *str;
  549. {
  550.   while (*str)
  551.     {
  552.       if (*str < '0' || *str > '9')
  553.     return 0;
  554.     }
  555.   return 1;
  556. }
  557.  
  558. /* Record a tag on the current line.
  559.   name is the tag name,
  560.   f is nonzero to use a pattern, zero to use line number instead. */
  561.  
  562. pfnote (name, f, linestart, linelen, lno, cno)
  563.      char *name;
  564.      logical f;            /* f == TRUE when function */
  565.      char *linestart;
  566.      int linelen;
  567.      int lno;
  568.      long cno;
  569. {
  570.   register char *fp;
  571.   register NODE *np;
  572.   char *altname;
  573.   char tem[51];
  574.  
  575.   if ((np = (NODE *) malloc (sizeof (NODE))) == NULL)
  576.     {
  577.       fprintf(stderr, "%s: too many entries to sort\n", progname);
  578.       put_entries(head);
  579.       free_tree(head);
  580.       head = NULL;
  581.       np = (NODE *) xmalloc(sizeof (NODE));
  582.     }
  583.   /* Change name "main" to M<thisfilename>. */
  584.   if (!eflag && !xflag && !strcmp(name, "main"))
  585.     {
  586.       fp = rindex(curfile, '/');
  587.       if (fp == 0)
  588.     fp = curfile;
  589.       else
  590.     fp++;
  591.       altname = concat ("M", fp, "");
  592.       fp = rindex(altname, '.');
  593.       if (fp && fp[2] == 0)
  594.     *fp = 0;
  595.       name = altname;
  596.     }
  597.   np->name = savestr(name);
  598.   np->file = curfile;
  599.   np->f = f;
  600.   np->lno = lno;
  601.   np->cno = cno;
  602.   np->left = np->right = 0;
  603.   if (eflag)
  604.     {
  605.       linestart[linelen] = 0;
  606.     }
  607.   else if (xflag == 0)
  608.     {
  609.       sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart);
  610.       linestart = tem;
  611.     }
  612.   np->pat = savestr (linestart);
  613.   if (head == NULL)
  614.     head = np;
  615.   else
  616.     add_node(np, head);
  617. }
  618.  
  619. free_tree(node)
  620.      NODE *node;
  621. {
  622.   while (node)
  623.     {
  624.       free_tree(node->right);
  625.       free(node);
  626.       node = node->left;
  627.     }
  628. }
  629.  
  630. void
  631. add_node(node, cur_node)
  632.      NODE *node,*cur_node;
  633. {
  634.   register int dif;
  635.  
  636.   dif = strcmp(node->name, cur_node->name);
  637.  
  638.   /* If this tag name matches an existing one, then
  639.      unless -e was given, do not add the node, but maybe print a warning */
  640.   if (!eflag && !dif)
  641.     {
  642.       if (node->file == cur_node->file)
  643.     {
  644.       if (!wflag)
  645.         {
  646.           fprintf(stderr,"%s: Duplicate entry in file %s, line %d: %s\n",
  647.               progname, node->file,lineno,node->name);
  648.           fprintf(stderr,"Second entry ignored\n");
  649.         }
  650.       return;
  651.     }
  652.       if (!cur_node->been_warned)
  653.     if (!wflag)
  654.       fprintf(stderr,"%s: Duplicate entry in files %s and %s: %s (Warning only)\n",
  655.           progname, node->file, cur_node->file, node->name);
  656.       cur_node->been_warned = TRUE;
  657.       return;
  658.     } 
  659.  
  660.   /* Actually add the node */
  661.   if (dif < 0) 
  662.     {
  663.       if (cur_node->left != NULL)
  664.     add_node(node,cur_node->left);
  665.       else
  666.     cur_node->left = node;
  667.       return;
  668.     }
  669.   if (cur_node->right != NULL)
  670.     add_node(node,cur_node->right);
  671.   else
  672.     cur_node->right = node;
  673. }
  674.  
  675. void
  676. put_entries(node)
  677.      reg NODE *node;
  678. {
  679.   reg char *sp;
  680.  
  681.   if (node == NULL)
  682.     return;
  683.  
  684.   /* Output subentries that precede this one */
  685.   put_entries (node->left);
  686.  
  687.   /* Output this entry */
  688.  
  689.   if (eflag)
  690.     {
  691.       fprintf (outf, "%s%c%d,%d\n",
  692.            node->pat, 0177, node->lno, node->cno);
  693.     }
  694.   else if (!xflag)
  695.     {
  696.       fprintf (outf, "%s\t%s\t",
  697.            node->name, node->file);
  698.  
  699.       if (node->f)
  700.     {        /* a function */
  701.       putc (searchar, outf);
  702.       putc ('^', outf);
  703.  
  704.       for (sp = node->pat; *sp; sp++)
  705.         {
  706.           if (*sp == '\\' || *sp == searchar)
  707.         putc ('\\', outf);
  708.           putc (*sp, outf);
  709.         }
  710.       putc (searchar, outf);
  711.     }
  712.       else
  713.     {        /* a typedef; text pattern inadequate */
  714.       fprintf (outf, "%d", node->lno);
  715.     }
  716.       putc ('\n', outf);
  717.     }
  718.   else if (vflag)
  719.     fprintf (stdout, "%s %s %d\n",
  720.          node->name, node->file, (node->lno+63)/64);
  721.   else
  722.     fprintf (stdout, "%-16s%4d %-16s %s\n",
  723.          node->name, node->lno, node->file, node->pat);
  724.  
  725.   /* Output subentries that follow this one */
  726.   put_entries (node->right);
  727. }
  728.  
  729. /* Return total number of characters that put_entries will output for
  730.  the nodes in the subtree of the specified node.
  731.  Works only if eflag is set, but called only in that case.  */
  732.  
  733. total_size_of_entries(node)
  734.      reg NODE *node;
  735. {
  736.   reg int total = 0;
  737.   reg long num;
  738.  
  739.   if (node == NULL)
  740.     return 0;
  741.  
  742.   /* Count subentries that precede this one */
  743.   total = total_size_of_entries (node->left);
  744.  
  745.   /* Count subentries that follow this one */
  746.   total += total_size_of_entries (node->right);
  747.  
  748.   /* Count this entry */
  749.  
  750.   total += strlen (node->pat) + 3;
  751.  
  752.   num = node->lno;
  753.   while (num)
  754.     {
  755.       total++;
  756.       num /= 10;
  757.     }
  758.  
  759.   num = node->cno;
  760.   if (!num) total++;
  761.   while (num)
  762.     {
  763.       total++;
  764.       num /= 10;
  765.     }
  766.   return total;
  767. }
  768.  
  769. /*
  770.  * This routine finds functions and typedefs in C syntax and adds them
  771.  * to the list.
  772.  */
  773.  
  774. #define CNL_SAVE_NUMBER \
  775. { \
  776.   linecharno = charno; lineno++; \
  777.   charno += 1 + readline (&lb, inf); \
  778.   lp = lb.buffer; \
  779. }
  780.  
  781. #define CNL \
  782. { \
  783.   CNL_SAVE_NUMBER; \
  784.   number = 0; \
  785. }
  786.  
  787. C_entries ()
  788. {
  789.   register int c;
  790.   register char *token, *tp, *lp;
  791.   logical incomm, inquote, inchar, midtoken;
  792.   int level;
  793.   char tok[BUFSIZ];
  794.  
  795.   lineno = 0;
  796.   charno = 0;
  797.   lp = lb.buffer;
  798.   *lp = 0;
  799.  
  800.   number = 0;
  801.   gotone = midtoken = inquote = inchar = incomm = FALSE;
  802.   level = 0;
  803.   tydef = none;
  804.   next_token_is_func = 0;
  805.  
  806.   while (!feof (inf))
  807.     {
  808.       c = *lp++;
  809.       if (c == 0)
  810.     {
  811.       CNL;
  812.       gotone = FALSE;
  813.     }
  814.       if (c == '\\')
  815.     {
  816.       c = *lp++;
  817.       if (c == 0)
  818.         CNL_SAVE_NUMBER;
  819.       c = ' ';
  820.     } 
  821.       else if (incomm)
  822.     {
  823.       if (c == '*')
  824.         {
  825.           while ((c = *lp++) == '*')
  826.         continue;
  827.           if (c == 0)
  828.         CNL;
  829.           if (c == '/')
  830.         incomm = FALSE;
  831.         }
  832.     }
  833.       else if (inquote)
  834.     {
  835.       /*
  836.       * Too dumb to know about \" not being magic, but
  837.       * they usually occur in pairs anyway.
  838.       */
  839.       if (c == '"')
  840.         inquote = FALSE;
  841.       continue;
  842.     }
  843.       else if (inchar)
  844.     {
  845.       if (c == '\'')
  846.         inchar = FALSE;
  847.       continue;
  848.     }
  849.       else switch (c)
  850.     {
  851.     case '"':
  852.       inquote = TRUE;
  853.       continue;
  854.     case '\'':
  855.       inchar = TRUE;
  856.       continue;
  857.     case '/':
  858.       if (*lp == '*')
  859.         {
  860.           lp++;
  861.           incomm = TRUE;
  862.         }
  863.       continue;
  864.     case '#':
  865.       if (lp == lb.buffer + 1)
  866.         number = 1;
  867.       continue;
  868.     case '{':
  869.       if (tydef == tag_ok)
  870.         {
  871.           tydef=middle;
  872.         }
  873.       level++;
  874.       continue;
  875.     case '}':
  876.       if (lp == lb.buffer + 1)
  877.         level = 0;    /* reset */
  878.       else
  879.         level--;
  880.       if (!level && tydef==middle)
  881.         {
  882.           tydef=end;
  883.         }
  884.       continue;
  885.     }
  886.       if (!level && !inquote && !incomm && gotone == FALSE)
  887.     {
  888.       if (midtoken)
  889.         {
  890.           if (endtoken(c))
  891.         {
  892.           int f;
  893.           char *buf = lb.buffer;
  894.           int endpos = lp - lb.buffer;
  895.           char *lp1 = lp;
  896.           int line = lineno;
  897.           long linestart = linecharno;
  898.           int tem;
  899.           if (linecharno != linecharno1) /* If not already saved */
  900.             {
  901.               /* Save line for later */
  902.               if (lb1.size < lb.size)
  903.             {
  904.               lb1.buffer = (char *) xrealloc(lb1.buffer, lb.size);
  905.               lb1.size = lb.size;
  906.             }
  907.               strcpy(lb1.buffer, lb.buffer);
  908.               linecharno1 = linecharno;
  909.             }
  910.           tem = consider_token (&lp1, token, &f, level);
  911.           lp = lp1;
  912.           if (tem)
  913.             {
  914.               if (linestart != linecharno)
  915.             {
  916.               strncpy (tok, token + (lb1.buffer - buf),
  917.                    tp-token+1);
  918.               tok[tp-token+1] = 0;
  919.               pfnote(tok, f, lb1.buffer, endpos, line, linestart);
  920.             }
  921.               else
  922.             {
  923.               strncpy (tok, token, tp-token+1);
  924.               tok[tp-token+1] = 0;
  925.               pfnote(tok, f, lb.buffer, endpos, line, linestart);
  926.             }
  927.               gotone = f;    /* function */
  928.             }
  929.           midtoken = FALSE;
  930.           token = lp - 1;
  931.         }
  932.           else if (intoken(c))
  933.         tp++;
  934.         }
  935.       else if (begtoken(c))
  936.         {
  937.           token = tp = lp - 1;
  938.           midtoken = TRUE;
  939.         }
  940.     }
  941.       if (c == ';'  &&  tydef==end)    /* clean with typedefs */
  942.     tydef=none;
  943.     }
  944. }
  945.  
  946. /*
  947.  * This routine  checks to see if the current token is
  948.  * at the start of a function, or corresponds to a typedef
  949.  * It updates the input line * so that the '(' will be
  950.  * in it when it returns.
  951.  */
  952. consider_token (lpp, token, f, level)
  953.      char **lpp, *token;
  954.      int *f, level;
  955. {
  956.   reg char *lp = *lpp;
  957.   reg char c;
  958.   logical firsttok;   /* T if have seen first token in ()'s */
  959.   int bad, win;
  960.  
  961.   *f = 1;            /* a function */
  962.   c = lp[-1];
  963.   bad = FALSE;
  964.   if (!number)
  965.     {        /* space is not allowed in macro defs    */
  966.       while (iswhite(c))
  967.     {
  968.       c = *lp++;
  969.       if (c == 0)
  970.         {
  971.           if (feof (inf))
  972.         break;
  973.           CNL;
  974.         }
  975.     }
  976.       /* the following tries to make it so that a #define a b(c)    */
  977.       /* doesn't count as a define of b.                */
  978.     }
  979.   else
  980.     {
  981.       number++;
  982.       if (number >= 4  || (number==2 && strncmp (token, "define", 6)))
  983.     {
  984.       gotone = TRUE;
  985.     badone:
  986.       bad = TRUE;
  987.       goto ret;
  988.     }
  989.     }
  990.   /* check for the typedef cases        */
  991.   if (tflag && istoken(token, "typedef", 7))
  992.     {
  993.       tydef=begin;
  994.       goto badone;
  995.     }
  996.   if (tydef==begin && (istoken(token, "struct", 6) ||
  997.                istoken(token, "union", 5) || istoken(token, "enum", 4)))
  998.   {
  999.     tydef=tag_ok;      
  1000.     goto badone;
  1001.   }
  1002.   if (tydef==tag_ok)
  1003.     {
  1004.       tydef=middle;
  1005.       goto badone;
  1006.     }
  1007.   if (tydef==begin)        /* e.g. typedef ->int<- */
  1008.     {
  1009.       tydef=end;
  1010.       goto badone;
  1011.     }
  1012.   if (tydef==middle && level == 0) /* e.g. typedef struct tag ->struct_t<- */
  1013.     {
  1014.       tydef=end;
  1015.     }
  1016.   if (tydef==end)
  1017.     {
  1018.       *f = 0;
  1019.       win = 1;
  1020.       goto ret;
  1021.     }
  1022.   /* Detect GNUmacs's function-defining macros. */
  1023.   if (!number && !strncmp (token, "DEF", 3))
  1024.      
  1025.     {
  1026.       next_token_is_func = 1;
  1027.       goto badone;
  1028.     }
  1029.   if (next_token_is_func)
  1030.     {
  1031.       next_token_is_func = 0;
  1032.       win = 1;
  1033.       goto ret;
  1034.     }
  1035.   if (c != '(')
  1036.     goto badone;
  1037.   firsttok = FALSE;
  1038.   while ((c = *lp++) != ')')
  1039.     {
  1040.       if (c == 0)
  1041.     {
  1042.       if (feof (inf))
  1043.         break;
  1044.       CNL;
  1045.     }
  1046.       /*
  1047.     * This line used to confuse ctags:
  1048.     *    int    (*oldhup)();
  1049.     * This fixes it. A nonwhite char before the first
  1050.     * token, other than a / (in case of a comment in there)
  1051.     * makes this not a declaration.
  1052.     */
  1053.       if (begtoken(c) || c=='/') firsttok++;
  1054.       else if (!iswhite(c) && !firsttok) goto badone;
  1055.     }
  1056.   while (iswhite (c = *lp++))
  1057.     {
  1058.       if (c == 0)
  1059.     {
  1060.       if (feof (inf))
  1061.         break;
  1062.       CNL;
  1063.     }
  1064.     }
  1065.   win = isgood (c);
  1066. ret:
  1067.   *lpp = lp - 1;
  1068.   return !bad && win;
  1069. }
  1070.  
  1071. /* Fortran parsing */
  1072.  
  1073. char    *dbp;
  1074. int    pfcnt;
  1075.  
  1076. PF_funcs(fi)
  1077.      FILE *fi;
  1078. {
  1079.   lineno = 0;
  1080.   charno = 0;
  1081.   pfcnt = 0;
  1082.  
  1083.   while (!feof (fi))
  1084.     {
  1085.       lineno++;
  1086.       linecharno = charno;
  1087.       charno += readline (&lb, fi) + 1;
  1088.       dbp = lb.buffer;
  1089.       if (*dbp == '%') dbp++ ;    /* Ratfor escape to fortran */
  1090.       while (isspace(*dbp))
  1091.     dbp++;
  1092.       if (*dbp == 0)
  1093.     continue;
  1094.       switch (*dbp |' ')
  1095.     {
  1096.     case 'i':
  1097.       if (tail("integer"))
  1098.         takeprec();
  1099.       break;
  1100.     case 'r':
  1101.       if (tail("real"))
  1102.         takeprec();
  1103.       break;
  1104.     case 'l':
  1105.       if (tail("logical"))
  1106.         takeprec();
  1107.       break;
  1108.     case 'c':
  1109.       if (tail("complex") || tail("character"))
  1110.         takeprec();
  1111.       break;
  1112.     case 'd':
  1113.       if (tail("double"))
  1114.         {
  1115.           while (isspace(*dbp))
  1116.         dbp++;
  1117.           if (*dbp == 0)
  1118.         continue;
  1119.           if (tail("precision"))
  1120.         break;
  1121.           continue;
  1122.         }
  1123.       break;
  1124.     }
  1125.       while (isspace(*dbp))
  1126.     dbp++;
  1127.       if (*dbp == 0)
  1128.     continue;
  1129.       switch (*dbp|' ')
  1130.     {
  1131.     case 'f':
  1132.       if (tail("function"))
  1133.         getit();
  1134.       continue;
  1135.     case 's':
  1136.       if (tail("subroutine"))
  1137.         getit();
  1138.       continue;
  1139.     case 'p':
  1140.       if (tail("program"))
  1141.         {
  1142.           getit();
  1143.           continue;
  1144.         }
  1145.       if (tail("procedure"))
  1146.         getit();
  1147.       continue;
  1148.     }
  1149.     }
  1150.   return (pfcnt);
  1151. }
  1152.  
  1153. tail(cp)
  1154.      char *cp;
  1155. {
  1156.   register int len = 0;
  1157.  
  1158.   while (*cp && (*cp&~' ') == ((*(dbp+len))&~' '))
  1159.     cp++, len++;
  1160.   if (*cp == 0)
  1161.     {
  1162.       dbp += len;
  1163.       return (1);
  1164.     }
  1165.   return (0);
  1166. }
  1167.  
  1168. void
  1169. takeprec()
  1170. {
  1171.   while (isspace(*dbp))
  1172.     dbp++;
  1173.   if (*dbp != '*')
  1174.     return;
  1175.   dbp++;
  1176.   while (isspace(*dbp))
  1177.     dbp++;
  1178.   if (!isdigit(*dbp))
  1179.     {
  1180.       --dbp;        /* force failure */
  1181.       return;
  1182.     }
  1183.   do
  1184.     dbp++;
  1185.   while (isdigit(*dbp));
  1186. }
  1187.  
  1188. void
  1189. getit()
  1190. {
  1191.   register char *cp;
  1192.   char c;
  1193.   char nambuf[BUFSIZ];
  1194.  
  1195.   while (isspace(*dbp))
  1196.     dbp++;
  1197.   if (*dbp == 0 || !isalpha(*dbp))
  1198.     return;
  1199.   for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++)
  1200.     continue;
  1201.   c = cp[0];
  1202.   cp[0] = 0;
  1203.   strcpy(nambuf, dbp);
  1204.   cp[0] = c;
  1205.   pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
  1206.   pfcnt++;
  1207. }
  1208.  
  1209. /*
  1210.  * lisp tag functions
  1211.  * just look for (def or (DEF
  1212.  */
  1213.  
  1214. L_funcs (fi)
  1215.      FILE *fi;
  1216. {
  1217.   lineno = 0;
  1218.   charno = 0;
  1219.   pfcnt = 0;
  1220.  
  1221.   while (!feof (fi))
  1222.     {
  1223.       lineno++;
  1224.       linecharno = charno;
  1225.       charno += readline (&lb, fi) + 1;
  1226.       dbp = lb.buffer;
  1227.       if (dbp[0] == '(' && 
  1228.       (dbp[1] == 'D' || dbp[1] == 'd') &&
  1229.         (dbp[2] == 'E' || dbp[2] == 'e') &&
  1230.           (dbp[3] == 'F' || dbp[3] == 'f'))
  1231.     {
  1232.       while (!isspace(*dbp)) dbp++;
  1233.       while (isspace(*dbp)) dbp++;
  1234.       L_getit();
  1235.     }
  1236.     }
  1237. }
  1238.  
  1239. void
  1240. L_getit()
  1241. {
  1242.   register char *cp;
  1243.   char c;
  1244.   char nambuf[BUFSIZ];
  1245.  
  1246.   if (*dbp == 0) return;
  1247.   for (cp = dbp+1; *cp && *cp != '(' && *cp != ' '; cp++)
  1248.     continue;
  1249.   c = cp[0];
  1250.   cp[0] = 0;
  1251.   strcpy(nambuf, dbp);
  1252.   cp[0] = c;
  1253.   pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
  1254.   pfcnt++;
  1255. }
  1256.  
  1257. /*
  1258.  * Scheme tag functions
  1259.  * look for (def... xyzzy
  1260.  * look for (def... (xyzzy
  1261.  * look for (def ... ((...(xyzzy ....
  1262.  * look for (set! xyzzy
  1263.  */
  1264.  
  1265. static void get_scheme ();
  1266. Scheme_funcs (fi)
  1267.      FILE *fi;
  1268. {
  1269.   lineno = 0;
  1270.   charno = 0;
  1271.   pfcnt = 0;
  1272.  
  1273.   while (!feof (fi))
  1274.     {
  1275.       lineno++;
  1276.       linecharno = charno;
  1277.       charno += readline (&lb, fi) + 1;
  1278.       dbp = lb.buffer;
  1279.       if (dbp[0] == '(' && 
  1280.       (dbp[1] == 'D' || dbp[1] == 'd') &&
  1281.         (dbp[2] == 'E' || dbp[2] == 'e') &&
  1282.           (dbp[3] == 'F' || dbp[3] == 'f'))
  1283.     {
  1284.       while (!isspace(*dbp)) dbp++;
  1285.           /* Skip over open parens and white space */
  1286.           while (*dbp && (isspace(*dbp) || *dbp == '(')) dbp++;
  1287.       get_scheme ();
  1288.     }
  1289.       if (dbp[0] == '(' && 
  1290.       (dbp[1] == 'S' || dbp[1] == 's') &&
  1291.         (dbp[2] == 'E' || dbp[2] == 'e') &&
  1292.           (dbp[3] == 'T' || dbp[3] == 't') &&
  1293.                 (dbp[4] == '!' || dbp[4] == '!') &&
  1294.                   (isspace(dbp[5])))
  1295.     {
  1296.       while (!isspace(*dbp)) dbp++;
  1297.           /* Skip over white space */
  1298.           while (isspace(*dbp)) dbp++;
  1299.       get_scheme ();
  1300.     }
  1301.     }
  1302. }
  1303.  
  1304. static void
  1305. get_scheme()
  1306. {
  1307.   register char *cp;
  1308.   char c;
  1309.   char nambuf[BUFSIZ];
  1310.  
  1311.   if (*dbp == 0) return;
  1312.   /* Go till you get to white space or a syntactic break */
  1313.   for (cp = dbp+1; *cp && *cp != '(' && *cp != ')' && !isspace(*cp); cp++)
  1314.     continue;
  1315.   /* Null terminate the string there. */
  1316.   c = cp[0];
  1317.   cp[0] = 0;
  1318.   /* Copy the string */
  1319.   strcpy(nambuf, dbp);
  1320.   /* Unterminate the string */
  1321.   cp[0] = c;
  1322.   /* Announce the change */
  1323.   pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
  1324.   pfcnt++;
  1325. }
  1326.  
  1327. /* Find tags in TeX and LaTeX input files.  */
  1328.  
  1329. /* TEX_toktab is a table of TeX control sequences that define tags.
  1330.    Each TEX_tabent records one such control sequence.  */
  1331.  
  1332. struct TEX_tabent
  1333. {
  1334.   char *name;
  1335.   int len;
  1336. };
  1337.  
  1338. struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */
  1339.  
  1340. /* Default set of control sequences to put into TEX_toktab.
  1341.    The value of environment var TEXTAGS is prepended to this.  */
  1342.  
  1343. static char *TEX_defenv =
  1344.   ":chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem:typeout";
  1345.  
  1346. struct TEX_tabent *TEX_decode_env (); 
  1347.  
  1348. static char TEX_esc = '\\';
  1349. static char TEX_opgrp = '{';
  1350. static char TEX_clgrp = '}';
  1351.  
  1352. /*
  1353.  * TeX/LaTeX scanning loop.
  1354.  */
  1355.  
  1356. TEX_funcs (fi)
  1357.     FILE *fi;
  1358. {
  1359.   char *lasthit;
  1360.  
  1361.   lineno = 0;
  1362.   charno = 0;
  1363.   pfcnt = 0;
  1364.  
  1365.   /* Select either \ or ! as escape character.  */
  1366.   TEX_mode (fi);
  1367.  
  1368.   /* Initialize token table once from environment. */
  1369.   if (!TEX_toktab)
  1370.     TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
  1371.  
  1372.   while (!feof (fi))
  1373.     {
  1374.       lineno++;
  1375.       linecharno = charno;
  1376.       charno += readline (&lb, fi) + 1;
  1377.       dbp = lb.buffer;
  1378.       lasthit = dbp;
  1379.  
  1380.       while (!feof (fi))
  1381.     {    /* Scan each line in file */
  1382.       lineno++;
  1383.       linecharno = charno;
  1384.       charno += readline (&lb, fi) + 1;
  1385.       dbp = lb.buffer;
  1386.       lasthit = dbp;
  1387.       while (dbp = index (dbp, TEX_esc)) /* Look at each escape in line */
  1388.         {
  1389.           register int i;
  1390.  
  1391.           if (! *(++dbp))
  1392.         break;
  1393.           linecharno += dbp - lasthit;
  1394.           lasthit = dbp;
  1395.           i = TEX_Token (lasthit);
  1396.           if (0 <= i)
  1397.         {
  1398.           TEX_getit (lasthit, TEX_toktab[i].len);
  1399.           break;        /* We only save a line once */
  1400.         }
  1401.         }
  1402.     }
  1403.     }
  1404. }
  1405.  
  1406. #define TEX_LESC '\\'
  1407. #define TEX_SESC '!'
  1408. #define TEX_CMT  '%'
  1409.  
  1410. /* Figure out whether TeX's escapechar is '\\' or '!' and set grouping */
  1411. /* chars accordingly. */
  1412.  
  1413. TEX_mode (f)
  1414.      FILE *f;
  1415. {
  1416.   int c, skip_line = 0;
  1417.  
  1418.   while ((c = getc (f)) != EOF)
  1419.     {
  1420.       /* Skip to next line if we hit the TeX comment char. */
  1421.       if (c == TEX_CMT)
  1422.       skip_line = 1;
  1423.       else if (c == '\n')
  1424.       skip_line = 0;
  1425.       else if (! skip_line)
  1426.       if (c == TEX_LESC || c == TEX_SESC)
  1427.         break;
  1428.     }
  1429.  
  1430.   if (c == TEX_LESC)
  1431.     {
  1432.       TEX_esc = TEX_LESC;
  1433.       TEX_opgrp = '{';
  1434.       TEX_clgrp = '}';
  1435.     } 
  1436.   else
  1437.     {
  1438.       TEX_esc = TEX_SESC;
  1439.       TEX_opgrp = '<';
  1440.       TEX_clgrp = '>';
  1441.     }
  1442.   rewind (f);
  1443. }
  1444.  
  1445. /* Read environment and prepend it to the default string. */
  1446. /* Build token table. */
  1447.  
  1448. struct TEX_tabent *
  1449. TEX_decode_env (evarname, defenv)
  1450.      char *evarname;
  1451.      char *defenv;
  1452. {
  1453.   register char *env, *p;
  1454.   extern char *savenstr (), *index ();
  1455.  
  1456.   struct TEX_tabent *tab;
  1457.   int size, i;
  1458.  
  1459.   /* Append deafult string to environment. */
  1460.   env = (char *) getenv (evarname);
  1461.   if (!env)
  1462.     env = defenv;
  1463.   else
  1464.     env = concat (env, defenv, "");
  1465.  
  1466.   /* Allocate a token table */
  1467.   for (size = 1, p=env; p;)
  1468.     if ((p = index (p, ':')) && *(++p))
  1469.       size++;
  1470.   tab = (struct TEX_tabent *) xmalloc (size * sizeof (struct TEX_tabent));
  1471.  
  1472.   /* Unpack environment string into token table. Be careful about */
  1473.   /* zero-length strings (leading ':', "::" and trailing ':') */
  1474.   for (i = 0; *env;)
  1475.     {
  1476.       p = index (env, ':');
  1477.       if (!p)            /* End of environment string. */
  1478.     p = env + strlen (env);
  1479.       if (p - env > 0)
  1480.     {    /* Only non-zero strings. */
  1481.       tab[i].name = savenstr (env, p - env);
  1482.       tab[i].len = strlen (tab[i].name);
  1483.       i++;
  1484.     }
  1485.       if (*p)
  1486.     env = p + 1;
  1487.       else
  1488.     {
  1489.       tab[i].name = NULL;    /* Mark end of table. */
  1490.       tab[i].len = 0;
  1491.       break;
  1492.     }
  1493.     }
  1494.   return tab;
  1495. }
  1496.  
  1497. /* Record a tag defined by a TeX command of length LEN and starting at NAME.
  1498.    The name being defined actually starts at (NAME + LEN + 1).
  1499.    But we seem to include the TeX command in the tag name.  */
  1500.  
  1501. void
  1502. TEX_getit (name, len)
  1503.     char *name;
  1504.     int len;
  1505. {
  1506.   char *p = name + len;
  1507.   char nambuf[BUFSIZ];
  1508.  
  1509.   if (*name == 0) return;
  1510.  
  1511.   /* Let tag name extend to next group close (or end of line) */
  1512.   while (*p && *p != TEX_clgrp)
  1513.     p++;
  1514.   strncpy (nambuf, name, p - name);
  1515.   nambuf[p - name] = 0;
  1516.  
  1517.   pfnote (nambuf, TRUE, lb.buffer, strlen (lb.buffer), lineno, linecharno);
  1518.   pfcnt++;
  1519. }
  1520.  
  1521. /* If the text at CP matches one of the tag-defining TeX command names,
  1522.    return the index of that command in TEX_toktab.
  1523.    Otherwise return -1.  */
  1524.  
  1525. /* Keep the capital `T' in `Token' for dumb truncating compilers
  1526.    (this distinguishes it from `TEX_toktab' */
  1527. TEX_Token (cp)
  1528.     char *cp;
  1529. {
  1530.   int i;
  1531.  
  1532.   for (i = 0; TEX_toktab[i].len > 0; i++)
  1533.     if (strncmp (TEX_toktab[i].name, cp, TEX_toktab[i].len) == 0)
  1534.       return i;
  1535.   return -1;
  1536. }
  1537.  
  1538. /* Initialize a linebuffer for use */
  1539.  
  1540. void
  1541. initbuffer (linebuffer)
  1542.      struct linebuffer *linebuffer;
  1543. {
  1544.   linebuffer->size = 200;
  1545.   linebuffer->buffer = (char *) xmalloc (200);
  1546. }
  1547.  
  1548. /* Read a line of text from `stream' into `linebuffer'.
  1549.  Return the length of the line.  */
  1550.  
  1551. long
  1552. readline (linebuffer, stream)
  1553.      struct linebuffer *linebuffer;
  1554.      register FILE *stream;
  1555. {
  1556.   char *buffer = linebuffer->buffer;
  1557.   register char *p = linebuffer->buffer;
  1558.   register char *pend;
  1559.  
  1560.   pend = p + linebuffer->size;
  1561.  
  1562.   while (1)
  1563.     {
  1564.       int c = getc (stream);
  1565.       if (p == pend)
  1566.     {
  1567.       linebuffer->size *= 2;
  1568.       buffer = (char *) xrealloc (buffer, linebuffer->size);
  1569.       p += buffer - linebuffer->buffer;
  1570.       pend = buffer + linebuffer->size;
  1571.       linebuffer->buffer = buffer;
  1572.     }
  1573.       if (c < 0 || c == '\n')
  1574.     {
  1575.       *p = 0;
  1576.       break;
  1577.     }
  1578.       *p++ = c;
  1579.     }
  1580.  
  1581.   return p - buffer;
  1582. }
  1583.  
  1584. char *
  1585. savestr(cp)
  1586.      char *cp;
  1587. {
  1588.   return savenstr (cp, strlen (cp));
  1589. }
  1590.  
  1591. char *
  1592. savenstr(cp, len)
  1593.     char *cp;
  1594.     int len;
  1595. {
  1596.   register char *dp;
  1597.  
  1598.   dp = (char *) xmalloc (len + 1);
  1599.   strncpy (dp, cp, len);
  1600.   dp[len] = '\0';
  1601.   return dp;
  1602. }
  1603.  
  1604. /*
  1605.  * Return the ptr in sp at which the character c last
  1606.  * appears; NULL if not found
  1607.  *
  1608.  * Identical to v7 rindex, included for portability.
  1609.  */
  1610.  
  1611. char *
  1612. rindex(sp, c)
  1613.      register char *sp, c;
  1614. {
  1615.   register char *r;
  1616.  
  1617.   r = NULL;
  1618.   do
  1619.     {
  1620.       if (*sp == c)
  1621.     r = sp;
  1622.     } while (*sp++);
  1623.   return(r);
  1624. }
  1625.  
  1626. /*
  1627.  * Return the ptr in sp at which the character c first
  1628.  * appears; NULL if not found
  1629.  *
  1630.  * Identical to v7 index, included for portability.
  1631.  */
  1632.  
  1633. char *
  1634. index(sp, c)
  1635.      register char *sp, c;
  1636. {
  1637.   do
  1638.     {
  1639.       if (*sp == c)
  1640.     return (sp);
  1641.     } while (*sp++);
  1642.   return (NULL);
  1643. }
  1644.  
  1645. /* Print error message and exit.  */
  1646.  
  1647. fatal (s1, s2)
  1648.      char *s1, *s2;
  1649. {
  1650.   error (s1, s2);
  1651.   exit (BAD);
  1652. }
  1653.  
  1654. /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
  1655.  
  1656. error (s1, s2)
  1657.      char *s1, *s2;
  1658. {
  1659.   fprintf (stderr, "%s: ", progname);
  1660.   fprintf (stderr, s1, s2);
  1661.   fprintf (stderr, "\n");
  1662. }
  1663.  
  1664. /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
  1665.  
  1666. char *
  1667. concat (s1, s2, s3)
  1668.      char *s1, *s2, *s3;
  1669. {
  1670.   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  1671.   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
  1672.  
  1673.   strcpy (result, s1);
  1674.   strcpy (result + len1, s2);
  1675.   strcpy (result + len1 + len2, s3);
  1676.   *(result + len1 + len2 + len3) = 0;
  1677.  
  1678.   return result;
  1679. }
  1680.  
  1681. /* Like malloc but get fatal error if memory is exhausted.  */
  1682.  
  1683. int
  1684. xmalloc (size)
  1685.      int size;
  1686. {
  1687.   int result = malloc (size);
  1688.   if (!result)
  1689.     fatal ("virtual memory exhausted", 0);
  1690.   return result;
  1691. }
  1692.  
  1693. int
  1694. xrealloc (ptr, size)
  1695.      char *ptr;
  1696.      int size;
  1697. {
  1698.   int result = realloc (ptr, size);
  1699.   if (!result)
  1700.     fatal ("virtual memory exhausted");
  1701.   return result;
  1702. }
  1703.